﻿package II.util {
	
	
	public class EvalReturn {
	
		// STATIC
		
		static public var TYP_UNKNOW = 0;
		static public var TYP_NUMBER = 1;
		static public var TYP_STRING = 2;
		static public var TYP_PARAMETERS = 3;
		static public var TYP_VARIABLE = 4;
		static public var TYP_NULL = 5;
		static public var TYP_BOOLEAN = 6;
		static public var TYP_OBJECT = 7;
		//static public var TYP_ARRAY = 8;

		// END STATIC

		
		
		// PROPERTIES
		
		// tipo del valore contenuto in value
		public function get type():Number {
			if ( this._type == EvalReturn.TYP_UNKNOW ) {
				if ( this.value == null ) {
					this._type = EvalReturn.TYP_NULL;
				} else {
					var num:Number = Number ( this.value );
					if ( isNaN ( num ) == false ) {
						this.value = num;
						this._type = EvalReturn.TYP_NUMBER;
					} else if ( this.value == "true" ) {
						this.value = true;
						this._type = EvalReturn.TYP_BOOLEAN;
					} else if ( this.value == "false" ) {
						this.value = false;
						this._type = EvalReturn.TYP_BOOLEAN;
					} else  {
						this._type = EvalReturn.TYP_VARIABLE;
					}
				}
			}
			return this._type;
		}
		public function set type(t:Number):void {
			this._type = t;
		}		
		private var _type:Number = TYP_UNKNOW;

		// valore
		public function get value():Object {
			return this._value;
		}
		public function set value(v:Object):void {
			this._value = v;
		}
		private var _value:Object = null;

		// valore in formato stringa
		public function get valueStr():String {
			if ( this.value != null ) return String(this.value);
			return "";
		}


		var objArray:Object = null;
		var keyArray:Object = null;
		
		// END PROPERTIES
		
		
		
		// CONSTRUCTORS
		
		public function EvalReturn(v:Object=null, t:Number=0){
			this.value = v;
			this.type = t;
		}
		
		// END CONSTRUCTORS
		
		
		
		
		
		
		
		
		
		public function operator ( oper:String, par:EvalReturn, objRef:Eval ):Boolean {
			par.resolveVariable ( objRef );
			this.resolveVariable ( objRef );
			
			if ( this.type == EvalReturn.TYP_NUMBER && par.type == EvalReturn.TYP_NUMBER ) {
				return this.operator_num ( oper, par );
			} else if ( this.type == EvalReturn.TYP_BOOLEAN || par.type == EvalReturn.TYP_BOOLEAN ) {
				return this.operator_boolean ( oper, par );
			} else if ( this.type == EvalReturn.TYP_STRING || par.type == EvalReturn.TYP_STRING ) {
				return this.operator_str ( oper, par );
			} 
			return null;
		}
		function operator_boolean ( oper:String, par:EvalReturn ):Boolean {
			var par1:Boolean =Boolean(this.value);
			var par2:Boolean = Boolean(par.value);
			switch ( oper ) {
				case ">":
					this.value = par1 > par2;
				break;
				case "<":
					this.value = par1 < par2;
				break;
				case "<=":
					this.value = par1 <= par2;
				break;
				case ">=":
					this.value = par1 >= par2;
				break;
				case "!=":
					this.value = par1 != par2;
				break;
				case "==":
					this.value = par1 == par2;
				break;
				case "&&":
					this.value = par1 && par2;
				break;
				case "||":
					this.value = par1 || par2;
				break;
				default:
					return false;
				break;
			}
			return true;
		}
		function operator_num ( oper:String, par:EvalReturn ):Boolean {
			var par1:Number =Number(this.value);
			var par2:Number = Number(par.value);
			switch ( oper ) {
				case "+":
					this.value = par1 + par2;
				break;
				case "-":
					this.value = par1 - par2;
				break;
				case "*":
					this.value = par1 * par2;
				break;
				case "/":
					this.value = par1 / par2;
				break;
				case ">":
					this.type = EvalReturn.TYP_BOOLEAN;
					this.value = par1 > par2;
				break;
				case "<":
					this.type = EvalReturn.TYP_BOOLEAN;
					this.value = par1 < par2;
				break;
				case "<=":
					this.type = EvalReturn.TYP_BOOLEAN;
					this.value = par1 <= par2;
				break;
				case ">=":
					this.type = EvalReturn.TYP_BOOLEAN;
					this.value = par1 >= par2;
				break;
				case "!=":
					this.type = EvalReturn.TYP_BOOLEAN;
					this.value = par1 != par2;
				break;
				case "==":
					this.type = EvalReturn.TYP_BOOLEAN;
					this.value = par1 == par2;
				break;
				case "&&":
					this.type = EvalReturn.TYP_BOOLEAN;
					this.value = par1 && par2;
				break;
				case "||":
					this.type = EvalReturn.TYP_BOOLEAN;
					this.value = par1 || par2;
				break;
				default:
					return false;
				break;
			}
			return true;
		}
		function operator_str ( oper:String, par:EvalReturn ):Boolean {
			var par1:String = this.valueStr;
			var par2:String = par.valueStr;
			switch ( oper ) {
				case "+":
					this.value = par1 + par2;
				break;
				case ">":
					this.type = EvalReturn.TYP_BOOLEAN;
					this.value = par1 > par2;
				break;
				case "<":
					this.type = EvalReturn.TYP_BOOLEAN;
					this.value = par1 < par2;
				break;
				case "<=":
					this.type = EvalReturn.TYP_BOOLEAN;
					this.value = par1 <= par2;
				break;
				case ">=":
					this.type = EvalReturn.TYP_BOOLEAN;
					this.value = par1 >= par2;
				break;
				case "!=":
					this.type = EvalReturn.TYP_BOOLEAN;
					this.value = par1 != par2;
				break;
				case "==":
					this.type = EvalReturn.TYP_BOOLEAN;
					this.value = par1 == par2;
				break;
				default:
					return false;
				break;
			}
			return true;
		}



		private static function ParInArray ( par:EvalReturn ) :Array {
			if ( par != null ) {
				var aPar:Array = new Array();
				if ( par.type == EvalReturn.TYP_PARAMETERS ) {
					var aValues = par.value;
					for ( var cont:Number = 0; cont < aValues.length; cont++ ) {
						aPar[cont] = aValues[cont].value;
					}
				} else {
					aPar.push ( par.value );
				}
				return aPar;
			}
			return null;
		}

		
		// esecuzione del valore come funzione
		function execute(par:EvalReturn, objRef:Eval):EvalReturn {
			var aPar:Array = EvalReturn.ParInArray ( par );
			var ref:Object = objRef.getRef( this.valueStr );
			var ret:Object;
			var retType:Number = EvalReturn.TYP_UNKNOW;
			if ( ref.refParent != null ) {
				ret = Object ( ref.refParent[ref.refName].apply ( ref.refParent, aPar ) );
			} else {
				trace( "errore non e' una funzione che si puo' eseguire");
				return null;
			}
			//if ( typeof( ret ) == "string" ) retType = EvalReturn.TYP_STRING;
			if ( ret instanceof String ) retType = EvalReturn.TYP_STRING;
			return new EvalReturn(ret, retType);
		}
		function createNew(par:EvalReturn, objRef:Eval):EvalReturn {
			var aPar:Array = EvalReturn.ParInArray ( par );
			var ref:Object = objRef.getRef( this.valueStr );
			var ret;
			var retType:Number = EvalReturn.TYP_UNKNOW;
			if ( ref.refParent != null ) {
				switch ( ref.refName ) {
					/*case "NodeBase":
						ret = Object(new NodeBase ( aPar[0] ));
					break;
					case "NodeReference":
						ret = Object(new NodeReference ( aPar[0], aPar[1] ));
					break;*/
					default:
						ret = Object ( ref.refParent[ref.refName].apply ( ref.refParent, aPar ) );
					break;
				}
				trace("createNew "+ ret );
			} else {
				trace( "errore non e' una funzione che si puo' eseguire");
				return null;
			}
			if ( typeof( ret ) == "string" ) retType = EvalReturn.TYP_STRING;
			return new EvalReturn(ret, retType);
		}
		function getArray(par:EvalReturn, objRef:Eval):EvalReturn {
			var ref:Object = objRef.getRef( this.valueStr );
			var evalRet:EvalReturn = new EvalReturn();
			evalRet.objArray = ref.refParent[ref.refName];
			evalRet.keyArray = par.value;
			
			if ( ref.refParent != null ) {
				evalRet.value = evalRet.objArray[evalRet.keyArray];
			} else {
				trace( "variabile non trovata");
				return null;
			}
			if ( typeof( evalRet.value ) == "string" ) evalRet.type = EvalReturn.TYP_STRING;
			
			return evalRet;
		}
		function assign ( par:EvalReturn, objRef:Eval ):void {
			if ( this.objArray != null ) {
				this.objArray[this.keyArray] = par.value;
				this.value = par.value;
			} else if ( this.type == EvalReturn.TYP_VARIABLE ) {
				objRef.setVariable( this.valueStr, par.value );
			} else {
				trace( "ERROR: syntax2 (nome variabile sbagliato)");
			}
		}
		function resolveVariable(objRef:Eval):void {
			
			if(  this.type == EvalReturn.TYP_PARAMETERS ) {
				for ( var cont:Number=0; cont<this.value.length; cont++ ) {
					this.value[cont].resolveVariable(objRef);
				}
				
			} else if ( this.type == EvalReturn.TYP_VARIABLE ) {
				var tmpValue:Object;
				if ( typeof( this.value ) == "string" ) {
					tmpValue = objRef.getVariable ( this.valueStr );
				} else {
					tmpValue = this.value;
				}
				switch ( typeof(tmpValue) ) {
					case "boolean":
						this.type = EvalReturn.TYP_BOOLEAN;
						this.value = tmpValue;
					break;
					case "number":
						this.type = EvalReturn.TYP_NUMBER;
						this.value = tmpValue;
					break;
					case "function":
						this.value = tmpValue;
					break;
					case "movieclip":
					case "object":
						this.type = EvalReturn.TYP_OBJECT;
						this.value = tmpValue;
					break;
					default:
						this.type = EvalReturn.TYP_STRING;
						this.value = tmpValue.toString();
					break;
				}
			}
		}

	}
}



